using System.Linq;
using Godot;

namespace Arcane.Source.Scripting
{
    public interface ICommand<T>
    {
        Variant Execute(T self);
    }

    public class CmdParam<T>(Variant v) : ICommand<T>
    {
        public Variant value = v;
        public Variant Execute(T self) => value;
    }

    public class CmdKV<T>(StringName key, ICommand<T> cmd) : ICommand<T>
    {
        public StringName Key => key;
        public Variant Execute(T self) => cmd.Execute(self);
    }

    public unsafe class Command<T>(delegate* <T, Variant[], Variant> fpCmd, ICommand<T>[] parameters) : ICommand<T>
    {
        public ICommand<T>[] parameters = parameters;
        public Variant Execute(T self) => fpCmd(self, parameters.Select(p => p.Execute(self)).ToArray());
    }

    public class CmdBlock<T>(params ICommand<T>[] commands) : ICommand<T>
    {
        public Variant Execute(T self) => commands.Aggregate(new Variant(), (v, cmd) => v = cmd.Execute(self));
    }

    public unsafe class CmdOp<T>(delegate* <Variant, Variant, Variant> fp, params ICommand<T>[] operands) : ICommand<T>
    {
        public ICommand<T>[] operands = operands;
        public Variant Execute(T self) 
            => operands
                .Skip(1)
                .Aggregate(
                    operands
                        .First()
                        .Execute(self),
                    (v, cmd) => fp(v, cmd.Execute(self)));
    }

    public class CmdIf<T> : ICommand<T>
    {
        public ICommand<T> cond, then, fin;
        public Variant Execute(T self)
            => cond.Execute(self).AsBool() ? then.Execute(self) : fin.Execute(self);
    }

    public unsafe class CmdGet<T>(delegate*<T, string, Variant> fpGet) : ICommand<T>
    {
        public string name;

        public Variant Execute(T self) => fpGet != null ? fpGet(self, name) : new Variant();
    }
}